﻿// Copyright (c) 2013 Klimov Ivan 
// Saint Petersburg State University
//
module BrahmaEigenCFA.EigenCFA

open Brahma.Helpers
open OpenCL.Net
open Brahma.OpenCL
open Brahma.FSharp.OpenCL.Core
open Microsoft.FSharp.Quotations
open Brahma.FSharp.OpenCL.Extensions
open BrahmaEigenCFA.StaticData

let Main () =

    let platformName = "*"

    let deviceType = DeviceType.Default

    let provider =
        try  ComputeProvider.Create(platformName, deviceType)
        with 
        | ex -> failwith ex.Message

    printfn "Using %A" provider

    let mutable commandQueue = new CommandQueue(provider, provider.Devices |> Seq.head)

    let storeDev = Array.zeroCreate(scaleExp * scaleM)
    let rep = Array.zeroCreate(1)

    let createStartStoreKernel = 
        <@
            fun (r:_2D) (devStore:array<_>) (scaleExp) (scaleM:int) (scaleVar:int) -> 
                let column = r.GlobalID0
                let row = r.GlobalID1

                if(row < scaleExp && column < scaleM) then 
                    if(row < scaleVar) then
                        if(column % scaleM = 0) then
                            devStore.[row*scaleM + column] <- 1
                        else
                            devStore.[row*scaleM + column] <- -1
                    else
                        if(column = 0) then
                            devStore.[row*scaleM + column] <- 2
                        else 
                            if(column = 1) then
                                devStore.[row*scaleM + column] <- row - scaleVar + 1
                            else 
                                devStore.[row*scaleM + column] <- -1 
        @>

    let createStartStoreKernel2 = 
        <@
            fun (r:_2D)
                (devFun:array<_>)
                (devArg1:array<_>)
                (devArg2:array<_>)
                (devStore:array<_>)
                (devRep:array<_>)
                devScaleM
                devScaleCall
                devScaleLam ->
                   let column = r.GlobalID0
                   if(column < devScaleCall && r.GlobalID1 < 2) then
                        let numCall = column
                        let Argi index =  
                            if(index = 0) then devArg1.[numCall]
                            else devArg2.[numCall]
                        let L index = devStore.[devFun.[numCall]*devScaleM + index]
                        let Li index = devStore.[(Argi r.GlobalID1)*devScaleM + index]
                        let rowStore row column = devStore.[row*devScaleM + column]
                        let vL j =
                            if(r.GlobalID1 = 0) then
                                (L j) - 1
                            else
                                (L j) - 1 + devScaleLam
                        for j in 1 .. ((L 0) - 1) do
                            for k in 1 .. ((Li 0) - 1) do
                                let mutable isAdd = 1
                                let addVar = (Li k)
                                for i in 1 .. ((rowStore (vL j) 0) - 1) do
                                    if((rowStore (vL j) i) = addVar) then 
                                        isAdd <- 0
                                if(isAdd > 0) then
                                    devRep.[0] <- devRep.[0] + 1
                                    let tail = (rowStore (vL j) 0)
                                    devStore.[(vL j)*devScaleM] <- devStore.[(vL j)*devScaleM] + 1
                                    devStore.[(vL j)*devScaleM + tail] <- addVar
        @>



//    printfn "Multiplying two %Ax%A matrices %A times using OpenCL and selected platform/device..." rows columns iterations

    let kernel, kernelPrepare, kernelRun = provider.Compile createStartStoreKernel
    let kernel2, kernelPrepare2, kernelRun2 = provider.Compile createStartStoreKernel2
    let d =(new _2D(scaleM, scaleExp, 1, 1))
    let d2 =(new _2D(scaleCall, 2, 1, 1))

    kernelPrepare d storeDev scaleExp scaleM scaleVar
    kernelPrepare2 d2 Fun Arg1 Arg2 storeDev rep scaleM scaleCall scaleLam
//    for i in 0 .. iterations - 1 do
    Timer<string>.Global.Start()
    let _ = commandQueue.Add(kernelRun()).Finish() 
    let _ = commandQueue.Add(kernelRun2()).Finish()            
    Timer<string>.Global.Lap("OpenCL")
    
    printfn "done."
    
    let _ = commandQueue.Add(storeDev.ToHost provider).Finish()

    Timer<string>.Global.Average("OpenCL") |> printfn "Avg. time, OpenCL: %A"

    printfStore storeDev scaleExp scaleM

    commandQueue.Dispose()
    provider.Dispose()
    provider.CloseAllBuffers()

    ignore (System.Console.Read())

do Main()